# @Time: 2024/4/9 20:19
# @Author: Yuanqing He
# @Email: heyuanqing007@163.com
import io
import pathlib
import sys
import threading
import zipfile
from datetime import datetime
from fastapi import FastAPI, Depends, UploadFile, HTTPException, Query, status
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import StreamingResponse, JSONResponse
from urllib.parse import quote_plus
from typing import List, Optional
import uvicorn
import os
import subprocess
import logging
import shutil
import json
from pathlib import Path
from pydantic import BaseModel
from xml.etree.ElementTree import ElementTree, Element, ParseError
from typing import Dict, Any

# 设置日志配置
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# 模型目录
ROOT_DIR = None

AbsolutePath = os.path.dirname(os.path.abspath(__file__))
# 模型配置信息
MODEL_LIST = []
MODEL_CONFIG = None
# 模型输入文件
INPUT_FILES = []
# 模型的输出文件
OUTPUT_FILES = []


# 定义通用函数
def prompt_user_for_input(prompt_text):
    """通用函数，用于提示用户输入"""
    return input(prompt_text).strip()


def load_model_list(json_file_path):
    """
    Load and return the contents of a JSON file containing model list data.

    Parameters:
    json_file_path (str): Path to the JSON file.

    Returns:
    list[dict]: A list of dictionaries representing the model list.
    """

    # Open and read the JSON file
    with open(json_file_path, 'r') as file:
        # Parse JSON content and store it in the variable MODEL_CONFIG
        model_list = json.load(file)

    return model_list


def _find_model_config_by_name(model_list: List[Dict], model_name: str) -> Optional[Dict]:
    """
    从模型列表中查找与指定名称匹配的模型配置。

    参数:
    - model_list: 列表，包含多个模型配置字典。
    - model_name: 字符串，要查找的模型名称。

    返回值:
    - 匹配的模型配置字典（如果存在），否则返回 None。
    """
    for model_config in model_list:
        if model_config["Model_Name"] == model_name:
            return model_config
    return None


def move_files_and_folders(data, output_folder='output'):
    """
    根据输入的字符串数组，移动当前文件夹下除input和output以外位置的对应文件夹或文件到output目录。

    参数:
    data (List[str]): 数据字符串数组，格式为 "类型-路径"，如 "0-outputfold", "1-test\outputfile.txt"。
    output_folder (str, optional): 输出文件夹名称，默认为 "output"。待移动的文件/文件夹将被移至此处。

    返回值:
    None
    """
    # 检查输出文件夹是否存在，不存在则创建
    output_folder = os.path.join(ROOT_DIR, output_folder)
    if not os.path.exists(output_folder):
        os.makedirs(output_folder)

    for item in data:
        # 根据“-”分割数据项，获取类型和路径
        type_, name = item.split("-")
        # 计算目标路径
        target_path = os.path.join(os.getcwd(), ROOT_DIR / Path(name))

        # 如果目标路径不存在，打印警告信息并跳过当前循环
        if not os.path.exists(target_path):
            print(f"Warning: {target_path} does not exist.")
            continue

        # 根据类型移动文件或文件夹
        if int(type_) == 0:  # 文件夹
            # 如果目标路径为文件夹，则移动到输出文件夹
            if os.path.isdir(target_path):
                shutil.move(target_path, output_folder)
            else:
                # 如果目标路径不是文件夹，打印警告信息
                print(f"Warning: {target_path} is not a directory.")
        elif int(type_) == 1:  # 文件
            # 如果目标路径为文件，则移动到输出文件夹
            if os.path.isfile(target_path):
                shutil.move(target_path, output_folder)
            else:
                # 如果目标路径不是文件，打印警告信息
                print(f"Warning: {target_path} is not a file.")
        else:
            # 如果类型无效，打印警告信息
            print(f"Warning: Invalid type '{type_}' for item {item}. Only '0' (folder) and '1' (file) are supported.")


def run_cmd(args, cwd=None):
    try:
        # 创建子进程，合并stderr到stdout
        proc = subprocess.Popen(
            args,
            cwd=cwd,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
            text=False,  # 使用二进制模式避免自动解码
            creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == "win32" else 0
        )

        output_buffer = []

        # 读取线程函数（二进制模式）
        def reader():
            while True:
                chunk = proc.stdout.read1(4096)  # 使用read1提高效率
                if not chunk:
                    break
                output_buffer.append(chunk)

        # 启动读取线程
        reader_thread = threading.Thread(target=reader)
        reader_thread.start()

        # 等待进程结束
        return_code = proc.wait()
        # 等待读取线程完成
        reader_thread.join()

        # 合并二进制数据并解码（优先尝试GBK，再尝试UTF-8）
        output_bytes = b''.join(output_buffer)
        try:
            output = output_bytes.decode('gbk').strip()
        except UnicodeDecodeError:
            output = output_bytes.decode('utf-8', errors='replace').strip()

        if return_code != 0:
            print(f"命令执行失败，返回码: {return_code}")
            print(f"输出内容: {output}")
            return None

        return output

    except Exception as e:
        print(f"执行命令时发生异常: {e}")
        return None


class TreeItem:
    def __init__(self, name: str, is_dir: bool, size: int, last_modified: float, children: List['TreeItem'] = None):
        self.filename = name
        self.is_dir = is_dir
        self.size = size
        self.last_modified = datetime.fromtimestamp(last_modified).isoformat()
        self.children = children if children is not None else []


def get_folder_info(folder_path: pathlib.Path) -> TreeItem:
    """
    获取指定文件夹的信息，并以树形结构返回。

    参数:
    folder_path: pathlib.Path - 指定的文件夹路径。

    返回值:
    TreeItem - 包含文件夹信息及其子项的树形结构对象。
    """
    # 创建树形结构项，初始化文件夹名称、是否为目录、大小和最后修改时间
    tree_item = TreeItem(folder_path.name, folder_path.is_dir(), folder_path.stat().st_size,
                         folder_path.stat().st_mtime)

    # 遍历文件夹中的所有条目
    for entry in folder_path.iterdir():
        # 如果是目录，则递归获取该目录的信息
        if entry.is_dir():
            child_tree_item = get_folder_info(entry)
            tree_item.children.append(child_tree_item)
        # 如果是文件，则直接创建文件的信息作为子项，并添加到树形结构中
        else:
            child_tree_item = TreeItem(entry.name, False, entry.stat().st_size, entry.stat().st_mtime)
            tree_item.children.append(child_tree_item)

    return tree_item


def get_dir(
        file_dir: Optional[str] = Query(None, title="Custom Download Root Directory"),
):
    """
    获取下载文件的目录路径。

    参数:
    - file_dir: 可选字符串，用户自定义的下载根目录。如果未提供，则使用默认根目录。

    返回值:
    - 返回一个Path对象，表示下载文件的根目录路径。

    异常:
    - 如果提供的自定义目录不存在，抛出HTTPException异常。
    """
    # 判断是否使用自定义下载目录
    if file_dir is None:
        return ROOT_DIR
    # 将自定义目录和默认根目录结合，形成完整的自定义下载目录路径
    dir = ROOT_DIR / Path(file_dir.strip())
    # 检查自定义目录是否存在
    if not os.path.isdir(dir):
        raise HTTPException(status_code=400, detail=f"Invalid custom root directory: {dir}")

    return dir


class OutputData(BaseModel):
    name: str
    type: str
    URL: str
    unzip: bool


def read_xml_config(file_path: str) -> OutputData:
    """从本地XML文件中读取配置信息"""
    try:
        tree = ElementTree(file=file_path)
        root = tree.getroot()

        # 提取ModelServices下的OutputData/Filedata元素
        filedata_element = next(iter(root.findall("OutputData/Filedata")), None)
        if filedata_element is None:
            raise ValueError("Invalid XML structure: OutputData/Filedata not found.")

        name = filedata_element.attrib["name"]
        type_ = filedata_element.attrib["type"]
        unzip = bool(filedata_element.attrib["unzip"])

        # 查找URL子元素
        url_element = filedata_element.find("URL")
        if url_element is None:
            raise ValueError("Invalid XML structure: URL element not found within Filedata.")

        if url_element.text is None:
            URL = ""
        else:
            URL = url_element.text

        return OutputData(name=name, type=type_, URL=URL, unzip=unzip)
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Error reading XML config: {str(e)}")


def find_model_server_docs():
    """
    查找当前目录下各文件夹内的DescriptionDocument/ModelServerDescriptionDocument.xml文件路径，
    并返回一个包含满足条件的文件夹名和XML文件路径的字典。
    """
    matches = {}  # 用于存储满足条件的文件夹名及其对应的XML文件路径

    # 获取当前工作目录
    current_dir = os.getcwd()

    # 遍历当前目录下的所有子目录
    for folder_name in os.listdir(current_dir):
        folder_path = os.path.join(current_dir, folder_name)

        # 确保是目录且不是.或..这样的特殊目录
        if os.path.isdir(folder_path) and folder_name not in ('.', '..'):
            description_doc_path = os.path.join(folder_path, 'DescriptionDocument')

            # 检查DescriptionDocument文件夹是否存在
            if os.path.isdir(description_doc_path):
                model_server_doc_path = os.path.join(description_doc_path, 'ModelServerDescriptionDocument.xml')

                # 检查ModelServerDescriptionDocument.xml文件是否存在
                if os.path.isfile(model_server_doc_path):
                    matches[folder_name] = model_server_doc_path  # 记录匹配的文件夹名和文件路径

    return matches


def read_operation_info(xml_path):
    """
    读取单个XML文件中的Operation节点信息及根节点的name属性（如果存在的话），并返回。

    :param xml_path: 单个XML文件路径
    :return: 一个字典，包含了XML文件的Operation节点信息及根节点的name属性（如果存在），如果未找到相关信息或发生错误则返回None
    """
    try:
        # 检查文件是否存在
        if not os.path.exists(xml_path) or not os.path.isfile(xml_path):
            print(f"文件'{xml_path}'不存在或不是一个有效的文件。")
            return None

        tree = ElementTree(file=xml_path)
        root = tree.getroot()

        # 确保root是一个Element对象
        if not isinstance(root, Element):
            print(f"解析文件'{xml_path}'时出现问题，无法获得有效的XML根节点。")
            return None

        # 安全地读取根节点的name属性
        model_name = root.get("name") if "name" in root.attrib else "Name attribute not found"

        # 查找Operation节点并读取信息
        operation_node = root.find("Operation")
        if operation_node is None:
            print(f"在文件'{xml_path}'中未找到Operation节点。")
            return None

        operation_info = {
            "Model_Name": model_name,
            "Execute_Program": operation_node.findtext("Execute_Program"),
            "Execute_Environment": operation_node.findtext("Execute_Environment"),
            "Parameters": [{"Name": param.text, "IsFile": param.get("isfile")}
                           for param in operation_node.findall("Parameters/Parameter")
                           if param.text is not None or "isfile" in param.attrib
                           ]
        }

        # 清理可能的None值
        operation_info = {k: v for k, v in operation_info.items() if v is not None or isinstance(v, str)}

        return operation_info
    except ParseError as pe:
        print(f"解析XML文件时发生错误: {pe}")
    except Exception as e:
        print(f"处理文件'{xml_path}'时发生未知错误: {e}")
    return None


def read_data(xml_file_path, data_type):
    """
    解析XML文件，读取InputData下的所有Filedata信息。

    :param xml_file_path: XML文件的路径
    :return: 包含所有Filedata信息的列表
    """
    filedatas = []  # 用于存储所有Filedata信息的列表

    try:
        # 解析XML文件
        tree = ElementTree(file=xml_file_path)
        root = tree.getroot()

        # 遍历InputData下的所有Filedata元素
        for filedata in root.find(data_type):
            # 提取Filedata的各个属性
            file_info = {
                'name': filedata.get('name'),
                'option': filedata.get('option'),
                'properties': filedata.get('properties'),
                'type': filedata.get('type'),
                'unzip': filedata.get('unzip'),
                'uploadpath': "",
                'description': filedata.get('description'),
                'schema': filedata.find('Schema').text if filedata.find('Schema') is not None else None,
                'url': filedata.find('URL').text if filedata.find('URL') is not None else None,
                'default': filedata.find('Default').text if filedata.find('Default') is not None else None,
                'supported': filedata.find('Supported').text if filedata.find('Supported') is not None else None,
            }
            filedatas.append(file_info)

    except ParseError as pe:
        print(f"解析XML文件时发生错误: {pe}")
    except FileNotFoundError:
        print(f"文件未找到: {xml_file_path}")

    return filedatas


class ModelAttribute(BaseModel):
    name: str
    abstract: str
    keywords: str
    type: str
    version: str
    provider: str
    email: str
    copyright: str


def read_model_attribute(xml_file_path):
    try:
        tree = ElementTree(file=xml_file_path)
        root = tree.getroot()
        # 获取模型属性信息
        model_services_attrs = root.attrib
        name = model_services_attrs.get("name")
        type = model_services_attrs.get("type")
        version = model_services_attrs.get("version")
        for attribute in root.find('AttributeSet'):
            if attribute.get("language") == "English":
                abstract = attribute.find('Abstract').text if attribute.find(
                    'Abstract') is not None else None
                keywords = attribute.find('Keyword').text if attribute.find(
                    'Keyword') is not None else None
                provider = attribute.find('Provider').text if attribute.find(
                    'Provider') is not None else None
                email = attribute.find('Email').text if attribute.find('Email') is not None else None
                copyright = attribute.find('Copyright').text if attribute.find(
                    'Copyright') is not None else None
                return {
                    "name": name,
                    "abstract": abstract,
                    "keywords": keywords,
                    "type": type,
                    "version": version,
                    "provider": provider,
                    "email": email,
                    "copyright": copyright
                }


    except ParseError as pe:
        print(f"解析XML文件时发生错误: {pe}")
    except FileNotFoundError:
        print(f"文件未找到: {xml_file_path}")


def read_xml_as_text(file_path):
    """
    读取并返回XML文件的全部内容作为字符串。

    参数:
    file_path (str): XML文件的路径。

    返回:
    str: XML文件的全部内容。
    """
    try:
        with open(file_path, 'r', encoding='utf-8') as file:
            xml_content = file.read()
            return xml_content
    except FileNotFoundError:
        print(f"错误：文件 {file_path} 未找到。")
        return None
    except Exception as e:
        print(f"读取文件时发生错误：{e}")
        return None


def init_model_info():
    matches = find_model_server_docs()
    for folder, xml_path in matches.items():
        operation_info = read_operation_info(xml_path)
        MODEL_LIST.append(operation_info)
    json_file_path = "MODEL_LIST.json"
    # 将MODEL_LIST转换为JSON字符串并存储在本地
    try:
        # 使用json.dump将数据写入到本地文件
        with open(json_file_path, 'w', encoding='utf-8') as json_file:
            json.dump(MODEL_LIST, json_file, ensure_ascii=False, indent=4)
        print(f"数据已成功保存至 {json_file_path}")
    except Exception as e:
        print(f"保存JSON文件时发生错误: {e}")


# 创建FastAPI应用实例
app = FastAPI(
    title="FastModelAPI",
    description="FastModelAPI interface documentation",
    version="1.0.0",
)
# 允许所有来源的跨域请求
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"]
)


# 定义路由
@app.get("/")
def read_root():
    return {"Hello": "Fast Model"}


# @app.post("/upload-file/")
# async def upload_file(file: UploadFile, custom_upload_dir: Optional[str] = Form(None)):
#     """
#     处理文件上传请求。
#
#     参数:
#     - file: UploadFile 类型，上传的文件。
#     - custom_upload_dir: 可选字符串，用户指定的上传目录，默认为None，若指定，则使用该目录。
#
#     返回值:
#     - 一个包含文件名、成功上传消息和文件上传至的目录路径的字典。
#     """
#     if ROOT_DIR is None:
#         raise HTTPException(status_code=400, detail=f"Model is not initialize.")
#
#     # 使用用户指定的上传目录（如果存在）或默认目录
#     target_dir = ROOT_DIR / Path(custom_upload_dir) if custom_upload_dir else ROOT_DIR
#
#     # 确保目标上传目录存在，若不存在则创建
#     target_dir.mkdir(parents=True, exist_ok=True)
#     # 构建存储上传文件的完整路径
#     target_path = target_dir / file.filename
#
#     # 将上传的文件内容写入目标路径
#     with open(target_path, 'wb') as out_file:
#         while content := await file.read(1024 * 1024):  # 以1MB为单位异步读取文件内容
#             out_file.write(content)  # 将读取的内容异步写入目标文件
#
#     # 返回包含文件信息和上传状态的成功响应
#     return {
#         "filename": file.filename,
#         "message": "File uploaded successfully",
#         "uploaded_to": str(target_path.parent)
#     }

@app.post("/upload-file/{file_name}/")
async def upload_file(file: UploadFile, file_name: str):
    # 查找与file_name匹配的文件信息
    current_file_info = next((f for f in INPUT_FILES if f["name"] == file_name), None)
    if not current_file_info:
        raise HTTPException(status_code=404, detail=f"No file configuration found for {file_name}")

    # 使用匹配到的文件信息
    if current_file_info["url"]:
        UPLOAD_DIR = ROOT_DIR / Path(current_file_info["url"])
    else:
        UPLOAD_DIR = ROOT_DIR

    # 确保目录存在
    if not UPLOAD_DIR.exists():
        os.makedirs(UPLOAD_DIR)

    # 保存上传的文件
    file_path = UPLOAD_DIR / Path(file.filename).name
    with open(file_path, "wb") as buffer:
        buffer.write(await file.read())

    # 根据配置解压文件（如果需要）
    if current_file_info["unzip"] and file_path.suffix == ".zip":
        try:
            with zipfile.ZipFile(file_path, 'r') as zip_ref:
                zip_ref.extractall(UPLOAD_DIR)
            # 可以考虑删除原始的.zip文件，根据需求调整
            os.remove(file_path)

        except zipfile.BadZipFile:
            raise HTTPException(status_code=400, detail="Uploaded file is not a valid zip file")

    # 更新description字段为文件的具体路径
    current_file_info["uploadpath"] = str(Path(AbsolutePath) / Path(file_path))

    return {
        "status": "success",
        "info": f"File '{file.filename}' uploaded and processed successfully."
    }


@app.get("/download-file/{filename:path}", response_class=StreamingResponse)
async def download_file(filename: str, file_dir: str = Depends(get_dir)):
    """
    异步处理文件下载请求。
    example: http://172.21.212.124:8000/download-file/output.txt?file_dir=output

    参数:
    - filename: 要下载的文件名，路径参数。
    - file_dir: 下载文件存储的目录，依赖注入获取。

    返回值:
    - StreamingResponse: 一个包含文件内容的响应流，设置为以附件形式下载文件。
    """

    # 获取完整文件路径
    file_path = os.path.join(file_dir, filename)

    # 检查文件是否存在
    if not os.path.isfile(file_path):
        logging.error(f"文件不存在: {file_path}")
        return {"error": "File not found"}

    # 对文件名进行URL编码以便在HTTP Header中正确传输
    quoted_filename = quote_plus(filename)

    # 设置HTTP头，包括Content-Disposition，这将提示浏览器下载文件而不是显示内容
    headers = {
        "Content-Disposition": f"attachment; filename*=UTF-8''{quoted_filename}",
    }

    # 创建一个异步生成器来读取文件内容
    async def file_stream():
        with open(file_path, mode="rb") as file:
            while chunk := file.read(1024 * 8):  # 每次读取8KB数据
                yield chunk

    # 返回文件流作为响应
    return StreamingResponse(file_stream(), headers=headers, media_type="application/octet-stream")


def download_result():
    path_list = []
    for file in INPUT_FILES:
        path_list.append(ROOT_DIR / Path(file["url"]) / Path(file["name"]))


@app.get("/download-results", response_class=StreamingResponse)
async def download_results():
    """
    下载所有INPUT_FILES中指定的文件，如果有多于一个文件，则打包成一个ZIP文件。
    """
    path_list = []
    for file in OUTPUT_FILES:
        if file["url"]:
            file_path = ROOT_DIR / Path(file["url"]) / Path(file["name"])
        else:
            raise HTTPException(status_code=404, detail=f"Files not found")
        path_list.append(file_path)

    # 检查文件是否存在
    missing_files = [str(path) for path in path_list if not path.exists()]
    if missing_files:
        raise HTTPException(status_code=404, detail=f"Files not found: {missing_files}")

    # 如果只有一个文件，直接返回该文件
    if len(path_list) == 1:
        file_path = path_list[0]
        if os.path.isdir(file_path):
            # 如果是文件夹，则打包为 zip
            zip_filename = f"{os.path.basename(file_path)}.zip"
            zip_buffer = io.BytesIO()
            with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zip_file:
                for root, _, files in os.walk(file_path):
                    for file in files:
                        zip_file.write(
                            os.path.join(root, file),
                            os.path.relpath(os.path.join(root, file), os.path.dirname(file_path))
                        )
            zip_buffer.seek(0)
            headers = {
                "Content-Disposition": f"attachment; filename*=UTF-8''{quote_plus(zip_filename)}"
            }
            return StreamingResponse(zip_buffer, headers=headers, media_type="application/zip")
        else:
            # 如果是普通文件，直接返回该文件
            file = open(file_path, "rb")
            content_disposition = f"attachment; filename={file_path.name}"
            headers = {
                "Content-Disposition": content_disposition
            }
            return StreamingResponse(file, headers=headers, media_type="application/octet-stream")
    # 如果有多个文件，打包成一个ZIP文件
    zip_buffer = io.BytesIO()
    with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zip_file:
        for file_path in path_list:
            zip_file.write(file_path, arcname=file_path.name)

    zip_buffer.seek(0)
    headers = {
        "Content-Disposition": "attachment; filename=downloaded_files.zip"
    }
    return StreamingResponse(zip_buffer, headers=headers, media_type="application/zip")


@app.get("/delete-file/{filename:path}")
async def delete_file(filename: str, file_dir: str = Depends(get_dir)):
    """
    异步删除指定文件。
    example: http://172.21.212.124:8000/delete-file/output.txt?file_dir=output

    参数:
    - filename: 要删除的文件名，路径相对依赖于file_dir参数指定的目录。
    - file_dir: 文件所在的目录，通过依赖注入获得。

    返回值:
    - 当文件成功删除时，返回一个包含成功消息的JSON响应，HTTP状态码为200。
    - 当文件不存在时，返回一个包含未找到消息的JSON响应，HTTP状态码为404。
    - 当删除文件过程中发生异常时，返回一个包含错误消息的JSON响应，HTTP状态码为500。
    """

    full_path = os.path.join(file_dir, filename)  # 拼接文件的完整路径

    try:
        if os.path.isfile(full_path):  # 检查文件是否存在
            os.remove(full_path)  # 如果文件存在，则删除
            return JSONResponse(status_code=status.HTTP_200_OK,
                                content={"message": f"File {filename} deleted successfully."})
        else:
            # 如果文件不存在，返回404状态码
            return JSONResponse(status_code=status.HTTP_404_NOT_FOUND,
                                content={"message": f"File {filename} not found."})
    except Exception as e:
        # 处理删除文件时可能出现的异常
        return JSONResponse(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                            content={"message": f"An error occurred while deleting the file: {str(e)}."})


@app.get("/get-data-info/{folder_name:path}")
async def get_data_info(folder_name: str):
    """
    异步函数，根据提供的文件夹路径获取该文件夹内的数据信息。

    参数:
    - folder_name: str，指定的文件夹名称，路径可以是相对路径或绝对路径。

    返回值:
    - 如果文件夹存在且为目录，则返回一个包含文件夹信息的字典。
    - 如果文件夹不存在或不是目录，则返回一个包含错误信息的字典。
    """

    # 将提供的文件夹名称转换为Path对象，并获取完整路径
    folder_path = pathlib.Path(folder_name)

    # 检查文件夹是否存在且为目录
    if not folder_path.exists():
        folder_path = ROOT_DIR
    elif not folder_path.is_dir():
        return {"error": "Folder not found"}
    else:
        folder_path = ROOT_DIR / folder_path

    try:
        # 尝试获取文件夹内的文件信息
        root_tree_item = get_folder_info(folder_path)

        return {"root": root_tree_item}
    except Exception as e:
        # 处理获取文件夹信息时可能出现的异常
        return {"error": f"An error occurred while processing: {str(e)}"}


@app.get("/get-model-list")
async def get_model_list():
    """
    获取模型列表。

    返回值:
    - 模型列表。
    """
    return MODEL_LIST


@app.get("/initialize-model/{model_name}")
async def initialize_model(model_name: str):
    """
    根据模型名称初始化模型配置。

    URL参数:
    - model_name: 字符串，模型名称。

    返回值:
    - 初始化成功的消息和模型配置信息（仅用于演示，实际应用中可省略）。
    """
    global MODEL_CONFIG
    global ROOT_DIR
    global INPUT_FILES
    global OUTPUT_FILES
    # 查找与模型名称匹配的模型配置
    matching_model_config = _find_model_config_by_name(MODEL_LIST, model_name)

    if matching_model_config:
        # 若找到匹配的模型配置，将其赋值给全局变量 MODEL_CONFIG\UPLOAD_DIR
        MODEL_CONFIG = matching_model_config
        ROOT_DIR = Path(f"{model_name}")
        # 获取模型输入数据信息
        INPUT_FILES = read_data(ROOT_DIR / Path("DescriptionDocument/ModelServerDescriptionDocument.xml"),
                                "InputData")
        # 获取模型的输出数据信息
        OUTPUT_FILES = read_data(ROOT_DIR / Path("DescriptionDocument/ModelServerDescriptionDocument.xml"),
                                 "OutputData")
        init_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        message = f"{init_time}：Model '{model_name}' initialized successfully."
        print(message)
    else:
        message = f"No model configuration found for '{model_name}'."
        print(message)
    return message


@app.post("/run-model")
async def run_model(params: Dict[str, Any]):
    """
    异步执行模型运行的接口。

    参数:·
    - params: 一个字典，包含模型运行所需的参数，键为参数名，值为参数值。

    返回值:
    - 返回字符串"success"，表示模型运行命令已成功提交。
    """
    # 检查模型是否已初始化
    if MODEL_CONFIG is None:
        raise HTTPException(status_code=400, detail=f"Model is not initialize.")

    params_array = []
    # 根据配置验证传入参数的完整性
    for param in MODEL_CONFIG["Parameters"]:
        if param["Name"] not in params:
            raise HTTPException(status_code=400, detail=f"Missing required parameter: {param}")

        if param["IsFile"] == "true":
            for file in INPUT_FILES:
                if file["name"] == params[param["Name"]]:
                    if file["uploadpath"] != "":
                        params_array.append(file["uploadpath"])
                    else:
                        raise HTTPException(status_code=400, detail=f"Missing required parameter file: {param}")
        else:
            params_array.append(str(params[param["Name"]]))

    # 拼接模型执行程序的完整路径
    model_exec_path = MODEL_CONFIG["Execute_Program"]

    # 执行环境
    execute_environment = MODEL_CONFIG["Execute_Environment"]
    command = [execute_environment, model_exec_path] + params_array

    # 执行模型运行命令
    run_cmd(command, ROOT_DIR)

    return "success"


@app.get("/download-result", response_class=StreamingResponse)
async def download_result():
    # 检查模型是否已初始化
    if MODEL_CONFIG is None:
        raise HTTPException(status_code=400, detail=f"Model is not initialize.")
    # 从XML文件中获取配置信息
    output_data = read_xml_config(ROOT_DIR / Path("DescriptionDocument/ModelServerDescriptionDocument.xml"))
    # 获取文件信息
    name = output_data.name
    file_type = output_data.type
    url = ROOT_DIR / Path(output_data.URL)
    is_folder = True if file_type == "folder" else False

    # 检查文件或文件夹是否存在
    if not os.path.exists(url):
        raise HTTPException(status_code=404, detail="File or folder not found")

    # 如果是文件夹，打包成zip
    if is_folder:
        zip_file_path = ROOT_DIR / Path(name)
        shutil.make_archive(zip_file_path, "zip", root_dir=url)
        file = open(zip_file_path.with_suffix(".zip"), "rb")
        content_disposition = f"attachment; filename={name}.zip"
    else:
        # 单个文件，直接读取
        file = open(os.path.join(url / Path(name)), "rb")
        content_disposition = f"attachment; filename={name}"

    headers = {
        "Content-Disposition": content_disposition
    }
    # 返回文件
    return StreamingResponse(file, headers=headers, media_type="application/octet-stream")


@app.get("/get-models-info", response_model=List[Dict[str, Any]])
async def get_server_models_info():
    models_info = []
    matches = find_model_server_docs()

    def process_xml_file(xml_path: str):
        try:
            directory_path = os.path.dirname(xml_path)
            data_path = os.path.join(directory_path, "OMF_Data.xml")
            file_path = os.path.join(directory_path, "OMF_File.xml")
            units_path = os.path.join(directory_path, "OMF_Units.xml")

            info = {
                "attribute_info": read_model_attribute(xml_path),
                "operation_info": read_operation_info(xml_path),
                "input_info": read_data(xml_path, "InputData"),
                "output_info": read_data(xml_path, "OutputData"),
                "OMF_Data": read_xml_as_text(data_path),
                "OMF_File": read_xml_as_text(file_path),
                "OMF_Units": read_xml_as_text(units_path)
            }
            return info
        except Exception as e:
            print(f"Error processing XML file: {xml_path}. Error: {str(e)}")
            return None

    # 由于process_xml_file不再是一个异步函数，我们不需要使用asyncio来并行处理
    for _, xml_path in matches.items():
        model_info = process_xml_file(xml_path)
        if model_info:
            models_info.append(model_info)

    return models_info


if __name__ == "__main__":
    # 1.Init MODELs
    print("****** Initialize the model information ******:\n")
    init_model_info()
    # Print the MODEL_LIST
    print(MODEL_LIST)
    # print(json.dumps(MODEL_CONFIG, indent=4))

    # 2.Start model server
    print("\n****** Activate Model Service ******:\n")
    uvicorn.run(app, host="0.0.0.0", port=8000)
